/*
 * Copyright (C) 2019 Adaptech s.r.o., Robert Pösel
 * Copyright (C) 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.googlecode.leptonica.android;

import android.graphics.Rect;
import android.util.Log;

import androidx.annotation.Size;

/**
 * Wrapper for Leptonica's native BOX.
 *
 * @author alanv@google.com (Alan Viverette)
 */
@SuppressWarnings("WeakerAccess")
public class Box {
	static {
		System.loadLibrary("jpeg");
		System.loadLibrary("png");
		System.loadLibrary("leptonica");
	}

	private static final String TAG = Box.class.getSimpleName();

	/**
	 * The index of the X coordinate within the geometry array.
	 */
	public static final int INDEX_X = 0;

	/**
	 * The index of the Y coordinate within the geometry array.
	 */
	public static final int INDEX_Y = 1;

	/**
	 * The index of the width within the geometry array.
	 */
	public static final int INDEX_W = 2;

	/**
	 * The index of the height within the geometry array.
	 */
	public static final int INDEX_H = 3;

	/**
	 * A pointer to the native Box object. This is used internally by native
	 * code.
	 */
	private final long mNativeBox;

	private boolean mRecycled = false;

	/**
	 * Creates a new Box wrapper for the specified native BOX.
	 *
	 * @param nativeBox A pointer to the native BOX.
	 */
	Box(long nativeBox) {
		mNativeBox = nativeBox;
		mRecycled = false;
	}

	/**
	 * Creates a box with the specified geometry. All dimensions should be
	 * non-negative and specified in pixels.
	 *
	 * @param x X-coordinate of the top-left corner of the box.
	 * @param y Y-coordinate of the top-left corner of the box.
	 * @param w Width of the box.
	 * @param h Height of the box.
	 */
	public Box(int x, int y, int w, int h) {
		if (x < 0 || y < 0 || w < 0 || h < 0) {
			throw new IllegalArgumentException("All box dimensions must be non-negative");
		}

		long nativeBox = nativeCreate(x, y, w, h);

		if (nativeBox == 0) {
			throw new OutOfMemoryError();
		}

		mNativeBox = nativeBox;
		mRecycled = false;
	}

	/**
	 * Returns a pointer to the native Box object.
	 *
	 * @return a pointer to the native Box object
	 */
	public long getNativeBox() {
		if (mRecycled)
			throw new IllegalStateException();

		return mNativeBox;
	}

	/**
	 * Returns the box's x-coordinate in pixels.
	 *
	 * @return The box's x-coordinate in pixels.
	 */
	public int getX() {
		if (mRecycled)
			throw new IllegalStateException();

		return nativeGetX(mNativeBox);
	}

	/**
	 * Returns the box's y-coordinate in pixels.
	 *
	 * @return The box's y-coordinate in pixels.
	 */
	public int getY() {
		if (mRecycled)
			throw new IllegalStateException();

		return nativeGetY(mNativeBox);
	}

	/**
	 * Returns the box's width in pixels.
	 *
	 * @return The box's width in pixels.
	 */
	public int getWidth() {
		if (mRecycled)
			throw new IllegalStateException();

		return nativeGetWidth(mNativeBox);
	}

	/**
	 * Returns the box's height in pixels.
	 *
	 * @return The box's height in pixels.
	 */
	public int getHeight() {
		if (mRecycled)
			throw new IllegalStateException();

		return nativeGetHeight(mNativeBox);
	}

	/**
	 * Returns an {@link android.graphics.Rect} containing the coordinates
	 * of this box.
	 *
	 * @return a rect representing the box
	 */
	public Rect getRect() {
		int[] geometry = getGeometry();
		int left = geometry[Box.INDEX_X];
		int top = geometry[Box.INDEX_Y];
		int right = left + geometry[Box.INDEX_W];
		int bottom = top + geometry[Box.INDEX_H];
		return new Rect(left, top, right, bottom);
	}

	/**
	 * Returns an array containing the coordinates of this box. See INDEX_*
	 * constants for indices.
	 *
	 * @return an array of box coordinates
	 */
	public int[] getGeometry() {
		int[] geometry = new int[4];

		if (getGeometry(geometry)) {
			return geometry;
		}

		return null;
	}

	/**
	 * Fills an array containing the coordinates of this box. See INDEX_*
	 * constants for indices.
	 *
	 * @param geometry A 4+ element integer array to fill with coordinates.
	 * @return <code>true</code> on success
	 */
	public boolean getGeometry(@Size(min = 4) int[] geometry) {
		if (mRecycled)
			throw new IllegalStateException();

		if (geometry.length < 4) {
			throw new IllegalArgumentException("Geometry array must be at least 4 elements long");
		}

		return nativeGetGeometry(mNativeBox, geometry);
	}

	public int getRefCount() {
		return nativeGetRefCount(mNativeBox);
	}

	/**
	 * Releases resources and frees any memory associated with this Box.
	 */
	public void recycle() {
		if (!mRecycled) {
			nativeDestroy(mNativeBox);

			mRecycled = true;
		}
	}

	// ***************
	// * NATIVE CODE *
	// ***************

	private static native long nativeCreate(int x, int y, int w, int h);

	private static native int nativeGetX(long nativeBox);

	private static native int nativeGetY(long nativeBox);

	private static native int nativeGetWidth(long nativeBox);

	private static native int nativeGetHeight(long nativeBox);

	private static native int nativeGetRefCount(long nativePix);

	private static native void nativeDestroy(long nativeBox);

	private static native boolean nativeGetGeometry(long nativeBox, int[] geometry);
}
